package com.aiti.lulian.pay.strategy.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.aiti.base.core.utils.stringUtil.StringUtil;
import com.aiti.lulian.pay.OrderStatusEnum;
import com.aiti.lulian.pay.config.WxPayConfig;
import com.aiti.lulian.pay.enums.wxpay.OrderStatus;
import com.aiti.lulian.pay.enums.wxpay.WxApiType;
import com.aiti.lulian.pay.enums.wxpay.WxNotifyType;
import com.aiti.lulian.pay.model.AliPayPreOrderData;
import com.aiti.lulian.pay.model.CardVipRecord;
import com.aiti.lulian.pay.model.PayInfo;
import com.aiti.lulian.pay.util.HttpClientUtils;
import com.aiti.lulian.pay.util.HttpUtils;
import com.aiti.lulian.pay.util.IpKit;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.wxpay.sdk.WXPayUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
@Service("weChatPayStrategy")
public class WeChatPayStrategy extends AbstractPayStrategy {
    private final ReentrantLock lock = new ReentrantLock();


    @Resource
    private WxPayConfig wxPayConfig;


    /**
     *
     * @param payInfo 支付参数
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AliPayPreOrderData genPayQrCode(PayInfo payInfo)  {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String ip = IpKit.getRealIp(request);
        if (StrUtil.isBlank(ip)) {
            ip = "127.0.0.1";
        }
        AliPayPreOrderData aliPayPreOrderData = new AliPayPreOrderData();
        log.info("生成订单");
        //生成用户相关的订单信息
        CardVipRecord orderInfo = createOrder(payInfo);


        log.info("调用统一下单API");

        HttpClientUtils client = new HttpClientUtils(wxPayConfig.getDomain().concat(WxApiType.NATIVE_PAY_V2.getType()));

        //组装接口参数
        Map<String, String> params = new HashMap<>();
        params.put("appid", wxPayConfig.getAppid());//关联的公众号的appid
        params.put("mch_id", wxPayConfig.getMchId());//商户号
        params.put("nonce_str", WXPayUtil.generateNonceStr());//生成随机字符串
        params.put("body", "微信支付");
        params.put("out_trade_no", orderInfo.getBaseId());//商户订单号

        //注意，这里必须使用字符串类型的参数（总金额：分）
        String totalFee = orderInfo.getPayAmount() + "";
        params.put("total_fee", totalFee);

        params.put("spbill_create_ip", ip);
        params.put("notify_url", wxPayConfig.getNotifyDomain().concat(WxNotifyType.NATIVE_NOTIFY_V2.getType()));
        params.put("trade_type", "NATIVE");
        try{
            //将参数转换成xml字符串格式：生成带有签名的xml格式字符串
            String xmlParams = WXPayUtil.generateSignedXml(params, wxPayConfig.getPartnerKey());
            log.info("\n xmlParams：\n" + xmlParams);

            client.setXmlParam(xmlParams);//将参数放入请求对象的方法体
            client.setHttps(true);//使用https形式发送
            client.post();//发送请求
            String resultXml = client.getContent();//得到响应结果
            log.info("\n resultXml：\n" + resultXml);
            //将xml响应结果转成map对象
            Map<String, String> resultMap = WXPayUtil.xmlToMap(resultXml);

            //错误处理
            if("FAIL".equals(resultMap.get("return_code")) || "FAIL".equals(resultMap.get("result_code"))){
                log.error("微信支付统一下单错误 ===> {} ", resultXml);
                throw new RuntimeException("微信支付统一下单错误");
            }

            //二维码
            String codeUrl = resultMap.get("code_url");
            orderInfo.setVxUrl(codeUrl);
            boolean b = updateOrder(orderInfo);
            if(!b){
                throw new RuntimeException("更新url失败");
            }



            //返回二维码
            Map<String, Object> map = new HashMap<>();
            map.put("codeUrl", codeUrl);
            aliPayPreOrderData.setCodeUrl(codeUrl);
            aliPayPreOrderData.setTradeNo(orderInfo.getBaseId());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return aliPayPreOrderData;
    }




    /**
     * 微信发送的回调
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    public String callBack(HttpServletRequest request, HttpServletResponse response) throws Exception {
        log.info("微信发送的回调");
        Map<String, String> returnMap = new HashMap<>();//应答对象

        //处理通知参数
        String body = HttpUtils.readData(request);

        //验签
        if(!WXPayUtil.isSignatureValid(body, wxPayConfig.getPartnerKey())) {
            log.error("通知验签失败");
            //失败应答
            returnMap.put("return_code", "FAIL");
            returnMap.put("return_msg", "验签失败");
            String returnXml = WXPayUtil.mapToXml(returnMap);
            return returnXml;
        }

        //解析xml数据
        Map<String, String> notifyMap = WXPayUtil.xmlToMap(body);
        //判断通信和业务是否成功
        if(!"SUCCESS".equals(notifyMap.get("return_code")) || !"SUCCESS".equals(notifyMap.get("result_code"))) {
            log.error("失败");
            //失败应答
            returnMap.put("return_code", "FAIL");
            returnMap.put("return_msg", "失败");
            String returnXml = WXPayUtil.mapToXml(returnMap);
            return returnXml;
        }

        //获取商户订单号
        String orderNo = notifyMap.get("out_trade_no");
        CardVipRecord outTradeNoOrderData = service.getOne(new QueryWrapper<CardVipRecord>().eq("base_id", orderNo));
        //并校验返回的订单金额是否与商户侧的订单金额一致
        if (outTradeNoOrderData != null && outTradeNoOrderData.getPayAmount() != Long.parseLong(notifyMap.get("total_fee"))) {
            log.error("金额校验失败");
            //失败应答
            returnMap.put("return_code", "FAIL");
            returnMap.put("return_msg", "金额校验失败");
            String returnXml = WXPayUtil.mapToXml(returnMap);
            return returnXml;
        }

        //处理订单
        if(lock.tryLock()){
            try {
                //处理重复的通知
                //接口调用的幂等性：无论接口被调用多少次，产生的结果是一致的。
                String orderStatus  = service.getOne(new QueryWrapper<CardVipRecord>().eq("base_id", orderNo)).getOrderStatus();
                if("0".equals(orderStatus) && OrderStatus.SUCCESS.getType().equals(notifyMap.get("result_code"))) {
                    outTradeNoOrderData.setOrderStatus(OrderStatusEnum.PAID.getStatus());
                    outTradeNoOrderData.setPayTime(DateUtil.parse(notifyMap.get("time_end")));
                    outTradeNoOrderData.setTradeNo(notifyMap.get("transaction_id"));
                    log.info("微信回调time_end:{}=======transaction_id:{}",notifyMap.get("time_end"), notifyMap.get("transaction_id"));
                    service.update(outTradeNoOrderData, new UpdateWrapper<CardVipRecord>().eq("base_id", outTradeNoOrderData.getBaseId()));

                    //获取到生成订单的时候存入数据库中的用户所对应的企业Id
                    String enterpriseId = outTradeNoOrderData.getEnterpriseId();
                    if(StringUtil.isNotBlank(enterpriseId)){
                        dataMigration(enterpriseId,outTradeNoOrderData.getVipLevel(),outTradeNoOrderData.getUserId(), 1, orderNo);
                    }
                    returnMap.put("return_code", "SUCCESS");
                    returnMap.put("return_msg", "OK");
                    String returnXml = WXPayUtil.mapToXml(returnMap);
                    log.info("支付成功，已应答");
                    return returnXml;


                }
            } finally {
                //要主动释放锁
                lock.unlock();
            }
        }
        returnMap.put("return_code", "SUCCESS");
        returnMap.put("return_msg", "OK");
        String returnXml = WXPayUtil.mapToXml(returnMap);
        return returnXml;


    }



    /**
     *
     * @param orderId 商户定单号
     * @return
     * @throws Exception
     */
    @Override
    public String queryOrderState(String orderId) throws Exception {
        log.info("查单接口调用 ===> {}", orderId);
        CardVipRecord outTradeNoOrderData = service.getOne(new QueryWrapper<CardVipRecord>().eq("base_id", orderId));
        if(outTradeNoOrderData != null && "1".equals(outTradeNoOrderData.getOrderStatus())){
            return "SUCCESS";
        }


        HttpClientUtils client = new HttpClientUtils(wxPayConfig.getDomain().concat("/pay/orderquery"));
        // 构建请求参数
        Map<String, String> params = new LinkedHashMap<>();
        params.put("appid", wxPayConfig.getAppid());
        params.put("mch_id", wxPayConfig.getMchId());
        params.put("out_trade_no", orderId); // 使用商户订单号查询
        params.put("nonce_str", WXPayUtil.generateNonceStr());

        //将参数转换成xml字符串格式：生成带有签名的xml格式字符串
        String xmlParams = WXPayUtil.generateSignedXml(params, wxPayConfig.getPartnerKey());
        log.info("\n xmlParams：\n" + xmlParams);

        client.setXmlParam(xmlParams);//将参数放入请求对象的方法体
        client.setHttps(true);//使用https形式发送
        client.post();//发送请求
        String resultXml = client.getContent();//得到响应结果
        log.info("\n resultXml：\n" + resultXml);
        String bodyAsString = "";
        //将xml响应结果转成map对象
        Map<String, String> resultMap = WXPayUtil.xmlToMap(resultXml);
        //错误处理
        if("FAIL".equals(resultMap.get("return_code")) || "FAIL".equals(resultMap.get("result_code"))){
            log.error("微信支付查询订单错误 ===> {} ", resultMap);
            throw new RuntimeException("微信支付查询订单错误");
        }
        log.info("微信支付查询订单成功, 返回结果 = {}", resultMap);

        if(OrderStatus.SUCCESS.name().equals(resultMap.get("trade_state"))){
            CardVipRecord cardVipRecord1 = new CardVipRecord();
            cardVipRecord1.setBaseId(orderId);
            cardVipRecord1.setOrderStatus("1");
            cardVipRecord1.setPayTime(DateUtil.parse(resultMap.get("time_end")));
            cardVipRecord1.setTradeNo(resultMap.get("transaction_id"));
            log.info("主动查询微信订单time_end:{}=======transaction_id:{}",resultMap.get("time_end"), resultMap.get("transaction_id"));
            updateOrder(cardVipRecord1);
        }
        bodyAsString = resultMap.get("trade_state");
        return bodyAsString;
    }




}
